/*
 *  Copyright 2008 Mark Ashworth <javameme@gmail.com>.
 * 
 *  Licensed under the GNU General Public License v3 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.gnu.org/licenses/gpl-3.0.html
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package xperiment.metaphor.service.user.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.dao.DataAccessException;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.security.event.authentication.AbstractAuthenticationEvent;
import org.springframework.security.event.authentication.AuthenticationSuccessEvent;
import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.event.authorization.AuthorizedEvent;
import xperiment.metaphor.model.people.user.User;
import xperiment.metaphor.service.user.LoggedInUserService;
import xperiment.metaphor.service.user.UserService;

/**
 * The default implementation of the LoggedInUserService
 * @author Mark Ashworth <javameme@gmail.com>
 * @version 1.0.0
 */
@ManagedResource
public class DefaultLoggedInUserService implements LoggedInUserService, ApplicationListener {
    private static final Log logger = LogFactory.getLog(DefaultLoggedInUserService.class);
    private Set<User> users = Collections.synchronizedSet(new HashSet<User>());
    private UserService userService;

    @Required
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    
    protected UserService getUserService() {
        return this.userService;
    }
    
    /**
     * Retrieves the users that are currently logged in
     * @return List<User>
     */
    @Override
    @ManagedAttribute(description="The logged in users of the system")
    public List<User> getLoggedInUsers() {
        return new ArrayList(users);
    }

    /**
     * Retrieves the user names that are currently logged in
     * @return String[]
     */
    @Override
    @ManagedAttribute(description="The logged in users of the system")
    public String[] getLoggedInUserNames() {
        
        synchronized(users) {
            String[] names = new String[users.size()];
            int i = 0;
            for (User user : users) {
                names[i++] = user.getName();
            }
            return names;
        }
    }
    
    /**
     * Called by the application context on every new ApplicationEvent. The
     * method listens for InteractiveAuthenticationSuccessEvent and
     * AuthenticationSuccessEvent to indicate that a user has logged in.
     * 
     * TODO: The logout event needs to be caught
     * @param event The application event
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof AbstractAuthenticationEvent) {
            AbstractAuthenticationEvent authEvent = (AbstractAuthenticationEvent) event;

            String userName = authEvent.getAuthentication().getName();
            if (authEvent instanceof InteractiveAuthenticationSuccessEvent || authEvent instanceof AuthenticationSuccessEvent) {
                try {
                    User user = getUserService().retrieveByUserName(userName);
                    users.add(user);
                } catch (DataAccessException e) {
                    logger.warn("Unable to resolve user name " + userName, e);
                }
            }
        } else if (event instanceof AuthorizedEvent) {
            /*
             * The authorized event is published when the user is logged
             * out by specifying the principal is anonymous. Need to find the
             * user name or id in the URL
             */
            AuthorizedEvent e = (AuthorizedEvent) event;
            logger.info(e.getAuthentication().getPrincipal());
        }
    }
}
